#ifndef __ICE_CONNECTION_H
#define __ICE_CONNECTION_H
#include "base/event_loop.h"
#include "ice/candidate.h"
#include "ice/udp_port.h"
#include "ice/stun.h"
#include "ice/ice_credentials.h"
#include "ice/stun_request.h"
#include "ice/ice_connection_info.h"
namespace xrtc
{
    class IceConnection;
    class ConnectionRequest : public StunRequest
    {
    public:
        ConnectionRequest(IceConnection *conn);
        void OnRequestResponse(StunMessage *msg) override;
        void OnRequestErrorResponse(StunMessage *msg) override;

    protected:
        void Prepare(StunMessage *msg) override;

    private:
        IceConnection *_connection;
    };

    class IceConnection : public sigslot::has_slots<>
    {
    public:
        enum WriteStateType
        {

            STATE_WRITABLE = 0,         // 最近发送ping binding request 能够收到响应
            STATE_WRITE_UNRELIABLE = 1, // 少量ping 没有收到响应
            STATE_WRITE_INIT = 2,       // 初始状态，尚未收到任何响应
            STATE_WRITE_TIMEOUT = 3

        };

        struct SentPing
        {

            SentPing(const std::string &id, int64_t ts) : id(id), sent_time(ts) {}
            std::string id;
            int64_t sent_time;
        };

        IceConnection(EventLoop *el, UDPPort *port, const Candidate &remote_candidate);
        ~IceConnection();
        UDPPort *Port() { return port_; }
        const Candidate &RemoteCandidate() const { return remote_candidate_; }
        const Candidate &LocalCandidate() const;
        void OnReadPacket(const char *buf, size_t len, int64_t ts);
        void HandleStunBindingRequest(StunMessage *stun_msg);
        void SendStunBindingResponse(StunMessage *stun_msg);
        void SendResponseMessage(const StunMessage &response);
        std::string ToString();
        WriteStateType WriteState() { return write_state_; }
        bool Writable() { return write_state_ == STATE_WRITABLE; }
        bool Receiving() { return receiving_; }
        bool Weak() { return !(Writable() && Receiving()); }
        bool Active() { return write_state_ != STATE_WRITE_TIMEOUT; }
        void MaybeSetRemoteIceParams(const IceParameters &ice_params);
        int64_t LastPingSent() const { return last_ping_sent_; }
        int NumPingsSent() const { return num_pings_sent_; }
        bool Stable(int64_t now) const;
        void Ping(int64_t now);
        uint64_t Priority();
        int Rtt() { return rtt_; }
        void SetSelected(bool value) { selected_ = value; }
        bool Selected() { return selected_; }
        void FailAndDestory();

        void SetState(IceCandidatePairState state);
        IceCandidatePairState State() { return state_; }
        void Destory();

        void OnConnectionRequestResponse(ConnectionRequest *request, StunMessage *msg);
        void OnConnectionRequestErrorResponse(ConnectionRequest *request, StunMessage *msg);
        void PrintPingsSinceLastResponse(std::string &pings, size_t max);
        void ReceivedPingResponse(int rtt);
        void UpdateReceiving(int64_t now);
        int ReceivingTtimeout();
        int64_t LastReceived();
        void SetWriteState(WriteStateType state);
        void UpdateState(int64_t now);
        int SendPacket(const char *data, size_t len);

        sigslot::signal1<IceConnection *> SignalStateChange;
        sigslot::signal1<IceConnection *> SignalConnectionDestory;
        sigslot::signal4<IceConnection *, const char *, size_t, int64_t> SignalReadPacket;

    private:
        void OnStunSendPacket(StunRequest *request, const char *buf, size_t size);
        bool MissResponse(int64_t now) const;
        bool TooLongWithoutResponse(int min_time, int64_t now);
        bool TooManyPingFails(size_t max_pings, int rtt, int64_t now);

    private:
        EventLoop *el_;
        UDPPort *port_;
        Candidate remote_candidate_;
        WriteStateType write_state_ = STATE_WRITE_INIT;
        bool receiving_ = false;
        int64_t last_ping_sent_ = 0;
        int64_t last_ping_received_ = 0;
        int64_t last_ping_response_received_ = 0;
        int64_t last_data_received_ = 0;
        int num_pings_sent_ = 0;
        std::vector<SentPing> pings_since_last_response_; // 存放都是未收到ping响应的集合
        StunRequestManager requests_;
        int rtt_ = 3000;      // ms
        int rtt_samples_ = 0; // sample num
        bool selected_ = false; // link selected
        IceCandidatePairState state_ = IceCandidatePairState::WAITING;
    };

} // namespace xrtc

#endif
